#!/usr/bin/env python3
"""
通用工具模块
包含GitCode和GitLab平台共享的工具函数和通用逻辑
"""

import json
import os
from datetime import datetime, timedelta
from typing import List, Dict, Optional, Any

import pandas as pd
import plotly.express as px
import streamlit as st
from loguru import logger

from platforms.gitcode import GitCodeInterface
from platforms.gitlab import GitLabInterface


class CommonUtils:
    """通用工具类"""

    @staticmethod
    def calculate_activity_threshold(df: pd.DataFrame, threshold_type: str = "median") -> int:
        """计算活跃度阈值
        
        Args:
            df: 包含提交数据的DataFrame
            threshold_type: 阈值类型 ('median', 'mean', 'q75')
            
        Returns:
            int: 活跃度阈值
        """
        if df.empty or 'author_name' not in df.columns:
            return 10  # 默认阈值

        commit_counts = df['author_name'].value_counts()

        if threshold_type == "median":
            return int(commit_counts.median())
        elif threshold_type == "mean":
            return int(commit_counts.mean())
        elif threshold_type == "q75":
            return int(commit_counts.quantile(0.75))
        else:
            return int(commit_counts.median())

    @staticmethod
    def render_sidebar_config():
        """渲染侧边栏配置"""
        from platforms.gitcode import GitCodeInterface
        from platforms.gitlab import GitLabInterface

        st.sidebar.title("🔧 配置面板")

        # 平台选择
        platform = st.sidebar.selectbox(
            "选择平台",
            ["GitCode", "GitLab"],
            index=0,
            help="选择要分析的Git平台",
            key="core_platform_select"
        )

        # 根据平台获取配置
        if platform == "GitCode":
            gitcode_interface = GitCodeInterface()
            access_token, org_name = gitcode_interface.render_sidebar_config()
            gitlab_url = ""
            group_id = ""
        else:  # GitLab
            gitlab_interface = GitLabInterface()
            access_token, gitlab_url, group_id = gitlab_interface.render_sidebar_config()
            org_name = ""

        # 时间范围配置
        st.sidebar.subheader("📅 时间范围")

        # 预设时间范围选项
        time_range_options = {
            "最近1个月": timedelta(days=30),
            "最近3个月": timedelta(days=90),
            "最近6个月": timedelta(days=180),
            "最近1年": timedelta(days=365),
            "自定义": None
        }

        selected_range = st.sidebar.selectbox(
            "选择时间范围",
            list(time_range_options.keys()),
            index=2,  # 默认选择最近6个月
            help="选择要分析的时间范围",
            key="core_time_range_select"
        )

        if selected_range == "自定义":
            col1, col2 = st.sidebar.columns(2)
            with col1:
                start_date = st.date_input(
                    "开始日期",
                    value=datetime.now() - timedelta(days=180),
                    help="分析的开始日期",
                    key="core_start_date"
                )
            with col2:
                end_date = st.date_input(
                    "结束日期",
                    value=datetime.now(),
                    help="分析的结束日期",
                    key="core_end_date"
                )
        else:
            end_date = datetime.now().date()
            start_date = (datetime.now() - time_range_options[selected_range]).date()
            st.sidebar.info(f"📅 时间范围: {start_date} 至 {end_date}")

        # 转换为datetime对象
        start_date = datetime.combine(start_date, datetime.min.time())

        return access_token, org_name, start_date.isoformat(), selected_range, platform, gitlab_url

    @staticmethod
    def load_user_name_mapping(file_path: str = "user_name_mapping.json") -> Dict[str, str]:
        """加载用户姓名映射
        
        Args:
            file_path: 映射文件路径
            
        Returns:
            Dict[str, str]: 用户名到真实姓名的映射
        """
        try:
            if os.path.exists(file_path):
                with open(file_path, 'r', encoding='utf-8') as f:
                    return json.load(f)
        except Exception as e:
            logger.warning(f"加载用户姓名映射失败: {e}")
        return {}

    @staticmethod
    def load_user_info_mapping(file_path: str = "email_name_mapping.csv") -> Dict[str, Dict[str, str]]:
        """加载用户信息映射（从CSV文件）
        
        Args:
            file_path: CSV映射文件路径
            
        Returns:
            Dict[str, Dict[str, str]]: 用户名到用户信息的映射
        """
        try:
            if os.path.exists(file_path):
                mapping_df = pd.read_csv(file_path)
                user_info_mapping = {}
                for _, row in mapping_df.iterrows():
                    username = row['username']
                    # 处理NaN值
                    name = row.get('name', '')
                    department = row.get('department', '')
                    status = row.get('status', '')

                    # 将NaN转换为空字符串
                    if pd.isna(name):
                        name = ''
                    if pd.isna(department):
                        department = ''
                    if pd.isna(status):
                        status = ''

                    user_info_mapping[username] = {
                        'name': str(name).strip() if name else '',
                        'department': str(department).strip() if department else '',
                        'status': str(status).strip() if status else ''
                    }
                return user_info_mapping
        except Exception as e:
            logger.warning(f"加载用户信息映射失败: {e}")
        return {}

    # 部门筛选功能已移除，GitLab统计不再支持部门分析

    @staticmethod
    def get_user_display_name(username: str, user_name_mapping: Dict[str, str]) -> str:
        """获取用户显示名称
        
        Args:
            username: 用户名
            user_name_mapping: 用户姓名映射
            
        Returns:
            str: 显示名称
        """
        return user_name_mapping.get(username, username)

    @staticmethod
    def format_time_range(start_date: datetime, end_date: datetime) -> str:
        """格式化时间范围
        
        Args:
            start_date: 开始日期
            end_date: 结束日期
            
        Returns:
            str: 格式化的时间范围字符串
        """
        return f"{start_date.strftime('%Y-%m-%d')} 至 {end_date.strftime('%Y-%m-%d')}"

    @staticmethod
    def create_metric_card(title: str, value: Any, delta: Optional[str] = None,
                           delta_color: str = "normal") -> None:
        """创建指标卡片
        
        Args:
            title: 指标标题
            value: 指标值
            delta: 变化值
            delta_color: 变化颜色
        """
        st.metric(label=title, value=value, delta=delta, delta_color=delta_color)

    @staticmethod
    def create_progress_bar(current: int, total: int, text: str = "") -> None:
        """创建进度条
        
        Args:
            current: 当前进度
            total: 总数
            text: 显示文本
        """
        progress = current / total if total > 0 else 0
        st.progress(progress, text=text)

    @staticmethod
    def safe_divide(numerator: float, denominator: float, default: float = 0.0) -> float:
        """安全除法
        
        Args:
            numerator: 分子
            denominator: 分母
            default: 默认值
            
        Returns:
            float: 除法结果
        """
        return numerator / denominator if denominator != 0 else default

    @staticmethod
    def format_number(num: float, precision: int = 2) -> str:
        """格式化数字
        
        Args:
            num: 数字
            precision: 精度
            
        Returns:
            str: 格式化后的数字字符串
        """
        if num >= 1000000:
            return f"{num / 1000000:.{precision}f}M"
        elif num >= 1000:
            return f"{num / 1000:.{precision}f}K"
        else:
            return f"{num:.{precision}f}"

    @staticmethod
    def get_color_palette(n_colors: int) -> List[str]:
        """获取颜色调色板
        
        Args:
            n_colors: 颜色数量
            
        Returns:
            List[str]: 颜色列表
        """
        if n_colors <= 10:
            return px.colors.qualitative.Set3[:n_colors]
        else:
            # 对于更多颜色，使用连续色谱
            return px.colors.sample_colorscale('viridis', n_colors)

    @staticmethod
    def validate_date_range(start_date: datetime, end_date: datetime) -> bool:
        """验证日期范围
        
        Args:
            start_date: 开始日期
            end_date: 结束日期
            
        Returns:
            bool: 是否有效
        """
        if start_date >= end_date:
            st.error("开始日期必须早于结束日期")
            return False

        # 检查日期范围是否过大（超过2年）
        if (end_date - start_date).days > 730:
            st.warning("日期范围过大，可能影响性能")

        return True

    @staticmethod
    def create_download_button(data: pd.DataFrame, filename: str,
                               button_text: str = "下载数据") -> None:
        """创建下载按钮
        
        Args:
            data: 要下载的数据
            filename: 文件名
            button_text: 按钮文本
        """
        csv = data.to_csv(index=False, encoding='utf-8-sig')
        st.download_button(
            label=button_text,
            data=csv,
            file_name=filename,
            mime='text/csv'
        )

    @staticmethod
    def display_dataframe_with_pagination(df: pd.DataFrame, page_size: int = 20,
                                          key: str = "pagination") -> None:
        """分页显示DataFrame
        
        Args:
            df: 要显示的DataFrame
            page_size: 每页大小
            key: 组件key
        """
        if df.empty:
            st.info("没有数据可显示")
            return

        total_rows = len(df)
        total_pages = (total_rows - 1) // page_size + 1

        if total_pages > 1:
            col1, col2, col3 = st.columns([1, 2, 1])
            with col2:
                page = st.selectbox(
                    f"页码 (共 {total_pages} 页, {total_rows} 条记录)",
                    range(1, total_pages + 1),
                    key=f"{key}_page_selector"
                )

            start_idx = (page - 1) * page_size
            end_idx = min(start_idx + page_size, total_rows)

            st.dataframe(
                df.iloc[start_idx:end_idx],
                use_container_width=True,
                hide_index=True
            )
        else:
            st.dataframe(df, use_container_width=True, hide_index=True)

    @staticmethod
    def create_summary_stats(df: pd.DataFrame, title: str = "数据概览") -> None:
        """创建数据概览统计
        
        Args:
            df: 数据DataFrame
            title: 标题
        """
        if df.empty:
            return

        st.subheader(title)

        col1, col2, col3, col4 = st.columns(4)

        with col1:
            st.metric("总记录数", len(df))

        with col2:
            if 'author_name' in df.columns:
                unique_authors = df['author_name'].nunique()
                st.metric("贡献者数量", unique_authors)

        with col3:
            if 'author_date' in df.columns:
                date_range = (df['author_date'].max() - df['author_date'].min()).days
                st.metric("时间跨度(天)", date_range)

        with col4:
            if 'total_changes' in df.columns:
                total_changes = df['total_changes'].sum()
                st.metric("总代码变更", f"{total_changes:,}")

    @staticmethod
    def create_data_overview(df: pd.DataFrame) -> Dict[str, Any]:
        """创建数据概览统计"""
        if df.empty:
            return {"total_rows": 0, "columns": [], "memory_usage": "0 KB"}

        return {
            "total_rows": len(df),
            "columns": df.columns.tolist(),
            "memory_usage": f"{df.memory_usage(deep=True).sum() / 1024:.2f} KB",
            "null_counts": df.isnull().sum().to_dict()
        }

    @staticmethod
    def handle_error(error: Exception, context: str = "") -> None:
        """统一错误处理
        
        Args:
            error: 异常对象
            context: 错误上下文
        """
        error_msg = f"{context}: {str(error)}" if context else str(error)
        st.error(error_msg)
        logger.error(f"错误 - {error_msg}", exc_info=True)

    @staticmethod
    def create_info_expander(title: str, content: str, expanded: bool = False) -> None:
        """创建信息展开器
        
        Args:
            title: 标题
            content: 内容
            expanded: 是否默认展开
        """
        with st.expander(title, expanded=expanded):
            st.info(content)

    @staticmethod
    def get_activity_thresholds(time_range: str):
        """根据时间周期计算活跃天数阈值"""
        # 时间周期对应的天数
        days_mapping = {
            "最近1个月": 30,
            "最近3个月": 90,
            "最近6个月": 180,
            "最近1年": 365
        }

        days = days_mapping.get(time_range, 365)

        # 基于实际开发活跃天数标准：
        # - 正常开发者：每周工作2-3天，年活跃天数100-150天
        # - 高活跃开发者：每周工作4-5天，年活跃天数200+天
        # - 低活跃：年活跃天数<100天
        normal_threshold = int(60 * days / 365)  # 正常活跃度阈值（年60天）
        high_threshold = int(200 * days / 365)  # 高活跃度阈值（年200天）

        return normal_threshold, high_threshold

    @staticmethod
    def load_username_name_mapping():
        """加载用户名姓名映射"""
        try:
            mapping_df = pd.read_csv('email_name_mapping.csv')
            return dict(zip(mapping_df['username'], mapping_df['name']))
        except FileNotFoundError:
            st.warning("未找到用户名姓名映射文件 email_name_mapping.csv")
            return {}
        except Exception as e:
            st.error(f"加载用户名姓名映射文件失败: {e}")
            return {}

    @staticmethod
    def get_display_name(username, name_mapping):
        """获取显示名称，优先使用映射的姓名，如果姓名为空则使用用户名"""
        if username in name_mapping:
            name = name_mapping[username]
            # 如果姓名不为空，返回姓名
            if name and str(name).strip():
                return str(name).strip()
        # 如果没有映射或姓名为空，返回用户名
        return username

    @staticmethod
    def display_repo_overview(df: pd.DataFrame, platform: str = "GitCode"):
        """显示仓库概览"""

        if platform == "GitCode":
            interface = GitCodeInterface()
            interface.display_repo_overview(df)
        else:  # GitLab
            interface = GitLabInterface()
            interface.display_repo_overview(df)

    @staticmethod
    def display_commits_analysis(df: pd.DataFrame, repo_name: str, platform: str = "GitCode"):
        """显示提交分析"""

        if platform == "GitCode":
            interface = GitCodeInterface()
            interface.display_commits_analysis(df, repo_name)
        else:  # GitLab
            interface = GitLabInterface()
            interface.display_commits_analysis(df, repo_name)

    @staticmethod
    def create_commit_charts(df: pd.DataFrame, repo_name: str):
        """创建提交相关的图表"""
        if df.empty:
            st.warning("没有数据可显示")
            return

        # 编程语言分布
        if 'language' in df.columns:
            st.subheader("💻 编程语言分布")
            language_counts = df['language'].value_counts().head(10)

            fig = px.pie(
                values=language_counts.values,
                names=language_counts.index,
                title=f"{repo_name} - 编程语言分布"
            )
            st.plotly_chart(fig, use_container_width=True)

        # Top 贡献者
        if 'author_name' in df.columns:
            st.subheader("👥 Top 贡献者")
            author_counts = df['author_name'].value_counts().head(10)

            fig = px.bar(
                x=author_counts.values,
                y=author_counts.index,
                orientation='h',
                title=f"{repo_name} - Top 10 贡献者",
                labels={'x': '提交次数', 'y': '贡献者'}
            )
            fig.update_layout(height=400)
            st.plotly_chart(fig, use_container_width=True)

        # 时间分布
        if 'author_date' in df.columns:
            st.subheader("📅 提交时间分布")

            col1, col2 = st.columns(2)

            with col1:
                # 月度趋势
                df['month'] = df['author_date'].dt.to_period('M')
                monthly_commits = df['month'].value_counts().sort_index()

                fig = px.line(
                    x=monthly_commits.index.astype(str),
                    y=monthly_commits.values,
                    title="月度提交趋势",
                    labels={'x': '月份', 'y': '提交次数'}
                )
                st.plotly_chart(fig, use_container_width=True)

            with col2:
                # 工作日分布
                if 'weekday' in df.columns:
                    weekday_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
                    weekday_counts = df['weekday'].value_counts().reindex(weekday_order, fill_value=0)

                    fig = px.bar(
                        x=weekday_order,
                        y=weekday_counts.values,
                        title="工作日提交分布",
                        labels={'x': '星期', 'y': '提交次数'}
                    )
                    st.plotly_chart(fig, use_container_width=True)

        # 代码变更分析
        if 'total_changes' in df.columns:
            st.subheader("📊 代码变更分析")

            col1, col2 = st.columns(2)

            with col1:
                # 变更量分布
                fig = px.histogram(
                    df,
                    x='total_changes',
                    nbins=20,
                    title="代码变更量分布",
                    labels={'x': '变更行数', 'y': '提交次数'}
                )
                st.plotly_chart(fig, use_container_width=True)

            with col2:
                # Top 变更者
                if 'author_name' in df.columns:
                    author_changes = df.groupby('author_name')['total_changes'].sum().sort_values(ascending=False).head(
                        10)

                    fig = px.bar(
                        x=author_changes.values,
                        y=author_changes.index,
                        orientation='h',
                        title="Top 10 代码变更者",
                        labels={'x': '总变更行数', 'y': '贡献者'}
                    )
                    fig.update_layout(height=400)
                    st.plotly_chart(fig, use_container_width=True)

    @staticmethod
    def display_overall_analysis(overall_data: List[pd.DataFrame], time_range: str):
        """显示整体分析结果 - 专注于提交者工作量管理"""
        if not overall_data:
            st.warning("没有找到任何提交数据")
            return

        # 加载用户名姓名映射
        username_name_mapping = CommonUtils.load_username_name_mapping()

        # 合并所有仓库的数据
        combined_df = pd.concat(overall_data, ignore_index=True)

        st.subheader("👥 提交者工作量统计")

        # 总体统计指标
        col1, col2, col3 = st.columns(3)

        with col1:
            total_commits = len(combined_df)
            st.metric("总提交数", total_commits)

        with col2:
            unique_authors = combined_df['author_email'].nunique() if 'author_email' in combined_df.columns else 0
            st.metric("提交者人数", unique_authors)

        with col3:
            unique_repos = combined_df['repo_name'].nunique() if 'repo_name' in combined_df.columns else 0
            st.metric("涉及项目", unique_repos)

        # 个人工作量统计
        if 'author_name' in combined_df.columns:
            st.subheader("📊 个人工作量排行")

            # 按提交数排行（使用邮箱）
            author_commits = combined_df['author_email'].value_counts().head(10)

            # 创建排行榜表格
            ranking_data = []
            for i, (author, commits) in enumerate(author_commits.items(), 1):
                display_name = CommonUtils.get_display_name(author, username_name_mapping)
                ranking_data.append({
                    "排名": i,
                    "提交者姓名": display_name,
                    "提交者邮箱": author,
                    "提交次数": commits,
                    "活跃度": "🔥" if commits > author_commits.mean() else "⚠️" if commits < author_commits.mean() * 0.5 else "✅"
                })

            ranking_df = pd.DataFrame(ranking_data)
            st.dataframe(ranking_df, use_container_width=True, hide_index=True)

            # 提交者活跃度分析
            st.subheader("⚠️ 提交者活跃度预警")
            st.info("注意：此处显示的是基于总提交数的简化分析，详细的活跃天数分析请查看'提交者详细分析'部分")

            # 使用统一的活跃度标准（这里仍使用提交数作为简化指标）
            normal_threshold, high_threshold = CommonUtils.get_activity_thresholds(time_range)

            # 找出活跃度低的提交者（基于总提交数的简化判断）
            all_authors = combined_df['author_email'].value_counts()
            low_activity_students = all_authors[all_authors <= normal_threshold]

            if not low_activity_students.empty:
                st.error(
                    f"⚠️ 发现 {len(low_activity_students)} 名提交者活跃度较低（总提交数 ≤ {normal_threshold}，年化<60次）")

                low_activity_data = []
                for student, commits in low_activity_students.items():
                    display_name = CommonUtils.get_display_name(student, username_name_mapping)
                    low_activity_data.append({
                        "提交者姓名": display_name,
                        "提交者邮箱": student,
                        "总提交次数": commits,
                        "活跃度标准": f"正常需>{normal_threshold}次",
                        "状态": "🔴 需要关注"
                    })

                low_activity_df = pd.DataFrame(low_activity_data)
                st.dataframe(low_activity_df, use_container_width=True, hide_index=True)
            else:
                st.success(f"✅ 所有提交者活跃度正常（均>{normal_threshold}次总提交数，年化60+）")

        # 时间分布分析
        if 'author_date' in combined_df.columns:
            st.subheader("⏰ 整体工作时间分析")

            col1, col2 = st.columns(2)

            with col1:
                if 'hour' in combined_df.columns:
                    hourly_commits = combined_df['hour'].value_counts().sort_index()

                    fig = px.bar(
                        x=hourly_commits.index,
                        y=hourly_commits.values,
                        title="每小时提交分布",
                        labels={'x': '小时', 'y': '提交次数'}
                    )
                    fig.update_layout(height=400)
                    st.plotly_chart(fig, use_container_width=True)

            with col2:
                if 'weekday' in combined_df.columns:
                    weekday_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
                    weekday_commits = combined_df['weekday'].value_counts().reindex(weekday_order, fill_value=0)

                    fig = px.bar(
                        x=weekday_order,
                        y=weekday_commits.values,
                        title="每周提交分布",
                        labels={'x': '星期', 'y': '提交次数'}
                    )
                    fig.update_layout(height=400)
                    st.plotly_chart(fig, use_container_width=True)
